package com.strato.annotation.osgi.v1_0_0.service.handler;

import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.osgi.framework.Bundle;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;

import com.strato.base.aop.v1_0_0.Invocation;
import com.strato.base.aop.v1_0_0.InvocationInterceptor;
import com.strato.base.cache.v1_0_0.TimedReference;

public class FieldProxyInvocationInterceptor implements InvocationInterceptor {

	private WeakReference<Bundle> bundleRef=null;
	private String query=null;
	private String interfaceName=null;
	private TimedReference<Object> targetRef=null;
	
	public FieldProxyInvocationInterceptor(Bundle bundle, String query, Class<?> intf) {
		this.interfaceName=intf.getName();
		this.bundleRef=new WeakReference<Bundle>(bundle);
		this.query=query;
	}
	
	public Object getTarget(){
		if(targetRef!=null&&!targetRef.isExpired()){
			Object target = targetRef.getObject();
			if(target!=null){
				return target;
			}
		}
		if(bundleRef==null){
			return null;
		}
		Bundle bundle=bundleRef.get();
		if(bundle==null){
			return null;
		}
		try {
			ServiceReference<?>[] srs = bundle.getBundleContext().getServiceReferences(interfaceName, query);
			if(srs==null||srs.length==0){
				return null;
			}
			ServiceReference<?> sr = srs[0];
			Object target=bundle.getBundleContext().getService(sr);
			targetRef=new TimedReference<Object>(target);
			return target;
		} catch (InvalidSyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Method method = invocation.getMethod();
		Object proxy=invocation.getObject();
		Object[] args = invocation.getArgs();
		if(method.getDeclaringClass().equals(Object.class)){
			return method.invoke(proxy, args);
		}
		Object target=getTarget();
		if(target==null){
			throw new NullPointerException("No service found for "+interfaceName+"(query:"+query+")");
		}
		try{
			return method.invoke(target, args);
		}catch(InvocationTargetException e){
			if(e.getCause()!=null){
				throw e.getCause();
			}else{
				throw e;
			}
		}
	}

}
